package com.maochd.cloud.excel.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson.JSONObject;
import com.maochd.cloud.api.file.service.RemoteFileService;
import com.maochd.cloud.common.core.constant.CommonConstant;
import com.maochd.cloud.common.core.domain.R;
import com.maochd.cloud.common.core.exception.BaseException;
import com.maochd.cloud.common.core.utils.file.FileUtil;
import com.maochd.cloud.common.core.utils.user.UserUtil;
import com.maochd.cloud.excel.constant.ExcelConstant;
import com.maochd.cloud.excel.convert.DateConverter;
import com.maochd.cloud.excel.convert.LocalDateConverter;
import com.maochd.cloud.excel.convert.LocalDateTimeConverter;
import com.maochd.cloud.excel.dto.ExportDto;
import com.maochd.cloud.excel.entity.ExportRecord;
import com.maochd.cloud.excel.handler.ColumnWidthHandler;
import com.maochd.cloud.excel.handler.CustomCellWriteHandler;
import com.maochd.cloud.excel.service.ExportService;
import com.maochd.cloud.excel.utils.ExcelThreadUtil;
import com.maochd.cloud.excel.utils.ExcelUtils;
import com.maochd.cloud.excel.utils.SpringUtils;
import com.maochd.cloud.excel.utils.StyleUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutableTriple;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ExportServiceImpl implements ExportService {

    @Resource
    private RemoteFileService remoteFileService;

    @Override
    public void exportAdapter(JSONObject po) {
        ExportDto dto = po.toJavaObject(ExportDto.class);
        ExportRecord exportRecord = ExportRecord.builder()
                .createDate(LocalDateTime.now())
                .exportType(dto.getExportType().getType())
                .fileName(StrUtil.isBlank(po.getString("filename")) ?
                        ExcelUtils.fileName(LocalDateTime.now(), dto.getExportType().getName())
                        : po.getString("filename"))
                .userId(UserUtil.userId())
                .operator(UserUtil.userName())
                .status(ExcelConstant.EXPORTING)
                .build();
        JSONObject queryCondition = po.getJSONObject("queryCondition");
        if (Objects.nonNull(queryCondition)) {
            exportRecord.setExportCondition(queryCondition.toJSONString());
        }
        exportRecord.insert();
        CompletableFuture.runAsync(() -> this.export(exportRecord, dto, po), ExcelThreadUtil.EXECUTOR);
    }

    private void export(ExportRecord exportRecord, ExportDto dto, JSONObject po) {
        ByteArrayInputStream in = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            // 休息1秒，防止覆盖OSS文件
            TimeUnit.SECONDS.sleep(CommonConstant.ONE);
            // 获得数据结果
            List<MutableTriple<? extends Class<?>, String, ? extends List<?>>>
                    mutableTriples = SpringUtils.invokeExport(dto.getExportType(), po);
            // 封装公共参数
            ExcelWriter excelWriter = EasyExcel.write(os).
                    registerConverter(new DateConverter()).
                    registerConverter(new LocalDateConverter()).
                    registerConverter(new LocalDateTimeConverter()).
                    registerWriteHandler(StyleUtils.globalWriteHandler()).
                    registerWriteHandler(new ColumnWidthHandler()).
                    registerWriteHandler(new CustomCellWriteHandler()).
                    build();
            for (MutableTriple<? extends Class<?>, String, ? extends List<?>> triple : mutableTriples) {
                excelWriter.write(triple.getRight(), EasyExcel.writerSheet(mutableTriples.indexOf(triple), triple.getMiddle()).
                        head(triple.getLeft()).
                        includeColumnFiledNames(SpringUtils.listAllField(triple.getLeft()).
                                stream().filter(f -> f.isAnnotationPresent(ExcelProperty.class)).
                                map(Field::getName).collect(Collectors.toSet())).
                        build());
            }
            excelWriter.finish();
            in = new ByteArrayInputStream(os.toByteArray());
            exportRecord.setStatus(ExcelConstant.EXPORT_SUCCESS);
            R<String> uploadResult = remoteFileService.uploadInner(FileUtil.InputStreamToMultipartFile(in,
                    exportRecord.getFileName()), ExcelUtils
                    .generateUploadFilename(ExcelConstant.EXPORT_PATH, exportRecord.getFileName()));
            if (R.isNotOk(uploadResult)) {
                throw new BaseException(uploadResult.getBizMsg());
            }
            exportRecord.setFileUrl(uploadResult.getData());
        } catch (Exception e) {
            log.error(ExcelConstant.EXPORT_ADAPTER_ERROR, e);
            exportRecord.setStatus(ExcelConstant.EXPORT_FAIL);
            exportRecord.setFailReason(e.getMessage());
        } finally {
            IoUtil.close(os);
            IoUtil.close(in);
        }
        // 更新导出记录
        exportRecord.updateById();
    }
}